package com.zkdcloud.shell.exec;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import org.apache.commons.cli.*;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;

/**
 * shell
 *
 * @author zk
 * @since 2019/5/24
 */
public class Shell {
    private static Options OPTIONS = new Options();
    private static CommandLine commandLine;
    private static String HELP_STRING = null;
    private static ExecInfo execInfo = new ExecInfo();

    public static void main(String[] args) {
        initCliArgs(args);
        try {
            String host = execInfo.getHost();
            String user = execInfo.getUser();
            String password = execInfo.getPassword();
            int port = execInfo.getPort();
            String command = execInfo.getCommand();

            JSch jsch = new JSch();
            Session session = jsch.getSession(user, host, port);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setPassword(password);
            session.connect();

            Channel channel = session.openChannel("exec");
            ((ChannelExec) channel).setCommand(command);

            channel.setInputStream(null);
            channel.setOutputStream(System.out);
            ((ChannelExec) channel).setErrStream(System.err);

            InputStream in = channel.getInputStream();

            channel.connect();

            byte[] tmp = new byte[1024];
            while (true) {
                while (in.available() > 0) {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) break;
                    System.out.print(new String(tmp, 0, i));
                }
                if (channel.isClosed()) {
                    if (in.available() > 0) continue;
                    if(channel.getExitStatus() != 0){
                        System.out.println("exit: " + channel.getExitStatus());
                    }

                    break;
                }
            }

            session.disconnect();
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * init args
     *
     * @param args args
     */
    private static void initCliArgs(String[] args) {
        // validate args
        {
            CommandLineParser commandLineParser = new DefaultParser();
            // help
            OPTIONS.addOption("help","usage help");
            // host
            OPTIONS.addOption(Option.builder("h").argName("hello").required().hasArg(true).longOpt("host").type(String.class).desc("the host of remote server").build());
            // port
            OPTIONS.addOption(Option.builder("P").hasArg(true).longOpt("port").type(Short.TYPE).desc("the port of remote server").build());
            // user
            OPTIONS.addOption(Option.builder("u").required().hasArg(true).longOpt("user").type(String.class).desc("the user of remote server").build());
            // password
            OPTIONS.addOption(Option.builder("p").required().hasArg(true).longOpt("password").type(String.class).desc("the password of remote server").build());
            // dstPath
            OPTIONS.addOption(Option.builder("c").required().hasArg(true).longOpt("command").type(String.class).desc("will exec command").build());
            try {
                commandLine = commandLineParser.parse(OPTIONS, args);
            } catch (ParseException e) {
                System.out.println(e.getMessage() + "\n" + getHelpString());
                System.exit(0);
            }
        }

        // init serverConfigure
        {
            if(commandLine.hasOption("help")){
                System.out.println("\n" + getHelpString());
                System.exit(1);
            }

            // host
            execInfo.setHost(commandLine.getOptionValue("h"));
            // port
            String portOptionValue = commandLine.getOptionValue("P");
            short port = portOptionValue == null || "".equals(portOptionValue) ? 22 : Short.parseShort(portOptionValue);
            execInfo.setPort(port);
            // user
            execInfo.setUser(commandLine.getOptionValue("u"));
            // password
            execInfo.setPassword(commandLine.getOptionValue("p"));
            // dstPath
            execInfo.setCommand(commandLine.getOptionValue("c"));
        }

    }

    /**
     * get string of help usage
     *
     * @return help string
     */
    private static String getHelpString() {
        if (HELP_STRING == null) {
            HelpFormatter helpFormatter = new HelpFormatter();

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            PrintWriter printWriter = new PrintWriter(byteArrayOutputStream);
            helpFormatter.printHelp(printWriter, HelpFormatter.DEFAULT_WIDTH, "scp -help", null,
                    OPTIONS, HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null);
            printWriter.flush();
            HELP_STRING = new String(byteArrayOutputStream.toByteArray());
            printWriter.close();
        }
        return HELP_STRING;
    }
    /**
     * exec info
     */
    private static class ExecInfo{
        /* connect info */
        String host;
        String user;
        String password;
        int port = 22;

        /* command */
        String command;

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public String getUser() {
            return user;
        }

        public void setUser(String user) {
            this.user = user;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        public String getCommand() {
            return command;
        }

        public void setCommand(String command) {
            this.command = command;
        }
    }
}
